package web_vg.implementation.hl_services.gui_service.components;

import tzhomestudio.framework.shared.utils.FileUtils;
import tzhomestudio.framework.shared.utils.UnzipUtils;
import vg_connector.interfaces.AnswerAdapter;
import vg_connector.interfaces.IClientListener;
import vg_connector.interfaces.actions.OpenGraphAction;
import vg_connector.interfaces.exceptions.ConnectException;
import web_vg.implementation.hl_services.main_service.WVGGlobals;
import web_vg.implementation.hl_services.main_service.WVGMainServiceHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

/**
 * @author Timur Zolotuhin (tzolotuhin@parallels.com)
 */
public class MainWindow {
    // Constants
    private static final String DOWNLOAD_LINK = "https://drive.google.com/uc?export=download&confirm=no_antivirus&id=0B9wWCF24t0wkUmNHMndaRnFBM2s";
    private static final String ZIP_FILE_NAME = "visual_graph.zip";
    private static final String VISUAL_GRAPH_FOLDER = "Visual Graph";

    private static final String RUN_FILE_NAME = "visual_graph.jar";

    private static final int NO_STATUS = 0;
    private static final int INSTALLING_STATUS = 1;
    private static final int INSTALLED_STATUS = 2;
    private static final int RUNNING_STATUS = 11;
    private static final int READY_STATUS = 30;

    private static final String INSTALL_TEXT = "Install";
    private static final String REINSTALL_TEXT = "Reinstall";
    private static final String RUN_TEXT = "Run";

    private static final String NO_INSTALL_STATUS_LABEL = "Visual Graph is not installed";
    private static final String INSTALLING_STATUS_LABEL = "Installing...";
    private static final String INSTALLED_STATUS_LABEL = "Visual Graph is installed";
    private static final String RUNNING_STATUS_LABEL = "Running...";
    private static final String READY_STATUS_LABEL = "Visual Graph is ready";

    // Main components
    private JPanel outView, innerView;

    private JLabel statusLabel, currentStatusLabel;

    private JLabel visualGraphHomeLabel;
    private JButton browseVisualGraphHomeButton;
    private JTextField visualGraphHomeTextField;

    private JButton installButton;
    private JButton runButton;
    private JButton openGraphExampleButton;

    // Main data
    private int currentStatus;
    private String visualGraphHome;

    public MainWindow() {
        visualGraphHome = WVGMainServiceHelper.config.getStringProperty(WVGGlobals.VG_PATH_ON_LOCAL_SYSTEM_KEY, FileUtils.getUserHomeDir() + VISUAL_GRAPH_FOLDER);
        WVGMainServiceHelper.config.save();

        if (new File(visualGraphHome + File.separator + RUN_FILE_NAME).exists())
            currentStatus = INSTALLED_STATUS;
        else
            currentStatus = NO_STATUS;

        // create components
        outView = new JPanel(new GridLayout(1, 1));
        innerView = new JPanel(new GridBagLayout());
        outView.add(innerView);

        statusLabel = new JLabel("Status: ");
        currentStatusLabel = new JLabel();

        visualGraphHomeLabel = new JLabel("Visual Graph Home directory: ");
        visualGraphHomeTextField = new JTextField();
        browseVisualGraphHomeButton = new JButton("...");

        installButton = new JButton(INSTALL_TEXT);
        runButton = new JButton("Run");
        openGraphExampleButton = new JButton("Open Example Graph");

        // build view
        rebuildView();

        // add listeners
        browseVisualGraphHomeButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                JFileChooser chooser = new JFileChooser();
                chooser.setDialogTitle("Browse Visual Graph home directory");
                chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                chooser.setAcceptAllFileFilterUsed(false);

                if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                    visualGraphHome = chooser.getSelectedFile().toString();
                    WVGMainServiceHelper.config.setProperty(WVGGlobals.VG_PATH_ON_LOCAL_SYSTEM_KEY, visualGraphHome);
                    WVGMainServiceHelper.config.save();

                    if (new File(visualGraphHome + File.separator + RUN_FILE_NAME).exists())
                        currentStatus = INSTALLED_STATUS;
                    else
                        currentStatus = NO_STATUS;

                    rebuildView();
                }
            }
        });

        installButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (isRan()) {
                    WVGMainServiceHelper.windowMessenger.errorMessage("Please, close other Visual Graph instances", "Error: Visual Graph is ran");
                } else {
                    int prevStatus = currentStatus;
                    currentStatus = INSTALLING_STATUS;
                    rebuildView();

                    WVGMainServiceHelper.logger.printInfo("Begin to create home directory: " + visualGraphHome);
                    File visualGraphDirectory = new File(visualGraphHome);
                    if (!visualGraphDirectory.exists()) {
                        if (!visualGraphDirectory.mkdirs()) {
                            WVGMainServiceHelper.windowMessenger.errorMessage("Can't create following folder: " + visualGraphDirectory.toString() + ". Please select another directory", "Error");
                            currentStatus = prevStatus;
                            rebuildView();
                            return;
                        }
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    WVGMainServiceHelper.logger.printInfo("Begin to remove all files in Visual Graph home directory");
                    try {
                        FileUtils.cleanDirectory(visualGraphHome);
                    } catch (IOException ex) {
                        WVGMainServiceHelper.windowMessenger.errorMessage("Can't clean following directory: " + visualGraphHome, "Error");
                        currentStatus = prevStatus;
                        rebuildView();
                        return;
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    WVGMainServiceHelper.logger.printInfo("Begin to download zip file");
                    try {
                        URL website = new URL(DOWNLOAD_LINK);
                        ReadableByteChannel rbc = Channels.newChannel(website.openStream());
                        FileOutputStream fos = new FileOutputStream(visualGraphHome + File.separator + ZIP_FILE_NAME);
                        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
                    } catch (Exception ex) {
                        WVGMainServiceHelper.windowMessenger.errorMessage("Can't download following file: " + DOWNLOAD_LINK + " Please, check the link", "Error");
                        currentStatus = prevStatus;
                        rebuildView();
                        return;
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    WVGMainServiceHelper.logger.printInfo("Extracting zip archive");
                    try {
                        UnzipUtils unzipUtils = new UnzipUtils(visualGraphHome + File.separator + ZIP_FILE_NAME, visualGraphHome);
                        unzipUtils.run();
                        String rootDir = unzipUtils.getRootFolder();
                        FileUtils.moveFilesFromDirectoryToAnotherDirectory(rootDir, visualGraphHome);
                    } catch (Exception ex) {
                        WVGMainServiceHelper.windowMessenger.errorMessage("Can't extract following file: " + visualGraphHome + File.separator + ZIP_FILE_NAME, "Error");
                        currentStatus = prevStatus;
                        rebuildView();
                        return;
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    currentStatus = INSTALLED_STATUS;
                    rebuildView();
                }
            }
        });

        runButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (isRan()) {
                    WVGMainServiceHelper.windowMessenger.errorMessage("Please, close other Visual Graph instances", "Error: Visual Graph is ran");
                } else {
                    int prevStatus = currentStatus;
                    currentStatus = RUNNING_STATUS;
                    rebuildView();

                    WVGMainServiceHelper.logger.printInfo("Begin to run Visual Graph");
                    try {
                        ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "javaw -jar " +
                                RUN_FILE_NAME +
                                " --" + WVGMainServiceHelper.CmdLineOptions.appInstanceIdOption.getAnyOpt() +
                                " " + WVGMainServiceHelper.CmdLineOptions.appInstanceIdOption.getInstanceId());

                        builder.directory(new File(visualGraphHome));
                        builder.redirectErrorStream(true);
                        builder.start();
                    } catch (IOException ex) {
                        WVGMainServiceHelper.windowMessenger.errorMessage("Can't run Visual Graph", "Error");
                        currentStatus = prevStatus;
                        rebuildView();
                        return;
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    WVGMainServiceHelper.logger.printInfo("Begin to connect to Visual Graph");
                    try {
                        WVGMainServiceHelper.clientAPIService.connect(WVGMainServiceHelper.CmdLineOptions.appInstanceIdOption.getInstanceId());
                    } catch (ConnectException ex) {
                        WVGMainServiceHelper.windowMessenger.errorMessage("Can't connect to Visual Graph", "Error");
                        currentStatus = prevStatus;
                        rebuildView();
                        return;
                    }
                    WVGMainServiceHelper.logger.printInfo("Done");

                    rebuildView();
                }
            }
        });

        openGraphExampleButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (isRan()) {
                    try {
                        OpenGraphAction action = new OpenGraphAction();

                        action.setFormat("graphml");
                        action.setContent(example);
                        action.setName("Sample");

                        WVGMainServiceHelper.clientAPIService.sendAction(action, new AnswerAdapter() {});
                    } catch (Exception ex) {
                        WVGMainServiceHelper.logger.printError(ex.getMessage(), ex);
                    }
                }
            }
        });

        WVGMainServiceHelper.clientAPIService.addClientListener(new IClientListener() {
            @Override
            public void connect() {
                if (!SwingUtilities.isEventDispatchThread()) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            connect();
                        }
                    });
                } else {
                    currentStatus = READY_STATUS;
                    rebuildView();
                }
            }

            @Override
            public void disconnect() {
                if (!SwingUtilities.isEventDispatchThread()) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            disconnect();
                        }
                    });
                } else {
                    currentStatus = INSTALLED_STATUS;
                    rebuildView();
                }
            }
        });
    }

    public JPanel getView() {
        return outView;
    }

//==============================================================================
//---------------PRIVATE METHODS------------------------------------------------
    private void rebuildView() {
        //clear panel
        innerView.removeAll();

        // packaging
        innerView.add(statusLabel, new GridBagConstraints(0,0,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0));
        innerView.add(currentStatusLabel, new GridBagConstraints(1,0,  2,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0));

        innerView.add(visualGraphHomeLabel, new GridBagConstraints(0,1,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0));
        innerView.add(visualGraphHomeTextField, new GridBagConstraints(1,1,  1,1,  1,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 3),  0,0));
        innerView.add(browseVisualGraphHomeButton, new GridBagConstraints(2,1,  1,1,  0,0,  GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(3, 3, 3, 3),  0,0));

        innerView.add(installButton, new GridBagConstraints(0,2,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 3),  0,0));
        innerView.add(runButton, new GridBagConstraints(1,2,  2,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 3),  0,0));
        innerView.add(openGraphExampleButton, new GridBagConstraints(0,3,  3,1,  1,0,  GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(3, 3, 3, 3),  0,0));

        // set status
        switch (currentStatus) {
            case NO_STATUS: {
                enableUI(true);

                installButton.setText(INSTALL_TEXT);
                runButton.setText(RUN_TEXT);

                runButton.setEnabled(false);
                openGraphExampleButton.setEnabled(false);

                currentStatusLabel.setText(NO_INSTALL_STATUS_LABEL);
                break;
            }

            case INSTALLING_STATUS: {
                enableUI(false);

                installButton.setText(INSTALL_TEXT);
                runButton.setText(RUN_TEXT);

                currentStatusLabel.setText(INSTALLING_STATUS_LABEL);
                break;
            }

            case INSTALLED_STATUS: {
                enableUI(true);

                installButton.setText(REINSTALL_TEXT);
                runButton.setText(RUN_TEXT);

                openGraphExampleButton.setEnabled(false);

                currentStatusLabel.setText(INSTALLED_STATUS_LABEL);
                break;
            }

            case RUNNING_STATUS: {
                enableUI(false);

                installButton.setText(REINSTALL_TEXT);
                runButton.setText(RUN_TEXT);

                currentStatusLabel.setText(RUNNING_STATUS_LABEL);
                break;
            }

            case READY_STATUS: {
                enableUI(false);

                installButton.setText(REINSTALL_TEXT);
                runButton.setText(RUN_TEXT);

                openGraphExampleButton.setEnabled(true);

                currentStatusLabel.setText(READY_STATUS_LABEL);
                break;
            }
        }

        visualGraphHomeTextField.setText(visualGraphHome);

        // update ui
        innerView.updateUI();
    }

    private void enableUI(boolean enable) {
        installButton.setEnabled(enable);
        runButton.setEnabled(enable);

        visualGraphHomeTextField.setEnabled(enable);
        browseVisualGraphHomeButton.setEnabled(enable);

        openGraphExampleButton.setEnabled(enable);
    }

    private boolean isInstalled() {
        return currentStatus >= INSTALLED_STATUS;
    }

    private boolean isRan() {
        return currentStatus >= READY_STATUS;
    }

    private static final String example = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"  \n" +
            "      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
            "      xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns \n" +
            "        http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n" +
            "  <key id=\"d0\" for=\"node\" attr.name=\"color\" attr.type=\"string\">\n" +
            "    <default>yellow</default>\n" +
            "  </key>\n" +
            "  <key id=\"d1\" for=\"edge\" attr.name=\"weight\" attr.type=\"double\"/>\n" +
            "  <graph id=\"G\" edgedefault=\"undirected\">\n" +
            "    <node id=\"n0\">\n" +
            "      <data key=\"d0\">green</data>\n" +
            "    </node>\n" +
            "    <node id=\"n1\"/>\n" +
            "    <node id=\"n2\">\n" +
            "      <data key=\"d0\">blue</data>\n" +
            "    </node>\n" +
            "    <node id=\"n3\">\n" +
            "      <data key=\"d0\">red</data>\n" +
            "    </node>\n" +
            "    <node id=\"n4\"/>\n" +
            "    <node id=\"n5\">\n" +
            "      <data key=\"d0\">turquoise</data>\n" +
            "    </node>\n" +
            "    <edge id=\"e0\" source=\"n0\" target=\"n2\">\n" +
            "      <data key=\"d1\">1.0</data>\n" +
            "    </edge>\n" +
            "    <edge id=\"e1\" source=\"n0\" target=\"n1\">\n" +
            "      <data key=\"d1\">1.0</data>\n" +
            "    </edge>\n" +
            "    <edge id=\"e2\" source=\"n1\" target=\"n3\">\n" +
            "      <data key=\"d1\">2.0</data>\n" +
            "    </edge>\n" +
            "    <edge id=\"e3\" source=\"n3\" target=\"n2\"/>\n" +
            "    <edge id=\"e4\" source=\"n2\" target=\"n4\"/>\n" +
            "    <edge id=\"e5\" source=\"n3\" target=\"n5\"/>\n" +
            "    <edge id=\"e6\" source=\"n5\" target=\"n4\">\n" +
            "      <data key=\"d1\">1.1</data>\n" +
            "    </edge>\n" +
            "  </graph>\n" +
            "</graphml>\n";
}
